{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## AutoGen Core\n",
    "\n",
    "This is agnostic to the underlying Agent framework\n",
    "\n",
    "You can use AutoGen AgentChat, or you can use something else; it's an Agent interaction framework.\n",
    "\n",
    "From that point of view, it's positioned similarly to LangGraph.\n",
    "\n",
    "#### The fundamental principle\n",
    "\n",
    "[Autogen Core](https://microsoft.github.io/autogen/stable//user-guide/core-user-guide/quickstart.html) decouples an agent's logic from how messages are delivered.  \n",
    "The framework provides a communication infrastructure, along with agent lifecycle, and the agents are responsible for their own work.\n",
    "\n",
    "The communication infrastructure is called a `Agent Runtime`.\n",
    "\n",
    "There are 2 types:\n",
    "- **[Standalone](https://microsoft.github.io/autogen/stable//user-guide/core-user-guide/core-concepts/architecture.html#standalone-agent-runtime)** - Standalone runtime is suitable for single-process applications where all agents are implemented in the same programming language and running in the same process\n",
    "- **[Distributed](https://microsoft.github.io/autogen/stable//user-guide/core-user-guide/core-concepts/architecture.html#distributed-agent-runtime)** - Distributed runtime is suitable for multi-process applications where agents may be implemented in different programming languages and running on different machines.\n",
    "\n",
    "This file focuses on standalone runtime: the **SingleThreadedAgentRuntime**, a local embedded agent runtime implementation.\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "# Loading up required libraries\n",
    "from autogen_core import AgentId, MessageContext, RoutedAgent, message_handler\n",
    "from autogen_core import SingleThreadedAgentRuntime\n",
    "from autogen_agentchat.agents import AssistantAgent\n",
    "from autogen_agentchat.messages import TextMessage\n",
    "from autogen_ext.models.openai import OpenAIChatCompletionClient\n",
    "\n",
    "from dotenv import load_dotenv\n",
    "from dataclasses import dataclass"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "# loading up environment variables from env file\n",
    "load_dotenv(override=True)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "#### Defining Message object\n",
    "\n",
    "This defines the structure we want for messages in our Agent framework."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "# Simple dataclass\n",
    "\n",
    "@dataclass\n",
    "class Message:\n",
    "    content: str"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "#### Defining our Agent\n",
    "\n",
    "A subclass of RoutedAgent.\n",
    "\n",
    "Every Agent has an **Agent ID** which has 2 components:  \n",
    "- `agent.id.type` describes the kind of agent it is  \n",
    "- `agent.id.key` gives it its unique identifier\n",
    "\n",
    "Any method with the `@message_handler` decorated will have the opportunity to receive messages.\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "class SimpleAgent(RoutedAgent):\n",
    "    def __init__(self) -> None:\n",
    "        super().__init__(\"A Simple Agent\")\n",
    "\n",
    "    @message_handler\n",
    "    async def on_message(self, message: Message, ctx: MessageContext) -> Message:\n",
    "        return Message(content=f\"This is {self.id.type}-{self.id.key}. You said {message.content}.\")"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "#### Creating a Standalone runtime and register our agent type"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "runtime = SingleThreadedAgentRuntime()\n",
    "await SimpleAgent.register(runtime, \"simple_agent\", lambda: SimpleAgent())"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "#### Starting runtime and sending a message"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "runtime.start()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "agent_id = AgentId(\"simple_agent\", \"default\")\n",
    "response = await runtime.send_message(Message(\"Hi there! How can I learn agentic ai?\"), agent_id)\n",
    "print(\">>>\", response.content)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "#### Remember to always stop and close the runtime"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "await runtime.stop()\n",
    "await runtime.close()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "#### Something more interesting!\n",
    "\n",
    "Using an AgentChat Assistant!"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "class LLMAgent(RoutedAgent):\n",
    "    def __init__(self) -> None:\n",
    "        super().__init__(\"LLMAgent\")\n",
    "        gemini_client = OpenAIChatCompletionClient(model=\"gemini-2.0-flash\")\n",
    "        self._delegate = AssistantAgent(\"LLMAgent\", model_client=gemini_client)\n",
    "\n",
    "    @message_handler\n",
    "    async def handle_message_type(self, message: Message, ctx: MessageContext) -> Message:\n",
    "        print(f\"{self.id.type} received message: {message.content}\")\n",
    "        text_message = TextMessage(content=message.content, source=\"user\")\n",
    "        response = await self._delegate.on_messages([text_message], ctx.cancellation_token)\n",
    "        reply = response.chat_message.content\n",
    "        print(f\"{self.id.type} responded: {reply}\")\n",
    "        return Message(content=reply)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "runtime = SingleThreadedAgentRuntime()\n",
    "await SimpleAgent.register(runtime, \"simple_agent\", lambda: SimpleAgent())\n",
    "await LLMAgent.register(runtime, \"LLMAgent\", lambda: LLMAgent())"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "runtime.start()\n",
    "response = await runtime.send_message(Message(\"How can I learn agentic ai?\"), AgentId(\"LLMAgent\", \"default\"))\n",
    "print(\">>>\", response.content)\n",
    "response =  await runtime.send_message(Message(response.content), AgentId(\"simple_agent\", \"default\"))\n",
    "print(\">>>\", response.content)\n",
    "response = await runtime.send_message(Message(response.content), AgentId(\"LLMAgent\", \"default\"))"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "#### Always remember to stop and close the runtime. This also applies if you want to re-run the cell above."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "await runtime.stop()\n",
    "await runtime.close()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "#### Introducing game player agents - having 3 agents interact!"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "from pyexpat import model\n",
    "\n",
    "\n",
    "class PlayerOneAgent(RoutedAgent):\n",
    "    def __init__(self, name: str) -> None:\n",
    "        super().__init__(name)\n",
    "        gemini_client = OpenAIChatCompletionClient(model=\"gemini-1.5-flash\", temperature=1.0)\n",
    "        self._delegate = AssistantAgent(name, model_client=gemini_client)\n",
    "\n",
    "    @message_handler\n",
    "    async def handle_message_type(self, message: Message, ctx: MessageContext) -> Message:\n",
    "        text_message = TextMessage(content=message.content, source=\"user\")\n",
    "        response = await self._delegate.on_messages([text_message], ctx.cancellation_token)\n",
    "        return Message(content=response.chat_message.content)\n",
    "\n",
    "class PlayerTwoAgent(RoutedAgent):\n",
    "    def __init__(self, name: str) -> None:\n",
    "        super().__init__(name)\n",
    "        gemini_client = OpenAIChatCompletionClient(model=\"gemini-2.0-flash\", temperature=1.0)\n",
    "        self._delegate = AssistantAgent(name, model_client=gemini_client)\n",
    "\n",
    "    @message_handler\n",
    "    async def handle_message_type(self, message: Message, ctx: MessageContext) -> Message:\n",
    "        text_message = TextMessage(content=message.content, source=\"user\")\n",
    "        response = await self._delegate.on_messages([text_message], ctx.cancellation_token)\n",
    "        return Message(content=response.chat_message.content)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "JUDGE = \"You are judging a game of rock, paper, scissors. The players have made these choices:\\n\"\n",
    "\n",
    "class RockPaperScissorsAgent(RoutedAgent):\n",
    "    def __init__(self, name: str) -> None:\n",
    "        super().__init__(name)\n",
    "        gemini_client = OpenAIChatCompletionClient(model=\"gemini-2.0-flash\", temperature=1.0)\n",
    "        self._delegate = AssistantAgent(name, model_client=gemini_client)\n",
    "\n",
    "    @message_handler\n",
    "    async def handle_message_type(self, message: Message, ctx: MessageContext) -> Message:\n",
    "            instruction = \"You are playing rock, paper, scissors. Respond only with the one word, one of the following: rock, paper, or scissors.\"\n",
    "            message = Message(content=instruction)\n",
    "            player_one = AgentId(\"player_one\", \"default\")\n",
    "            player_two = AgentId(\"player_two\", \"default\")\n",
    "            response1 = await self.send_message(message, player_one)\n",
    "            response2 = await self.send_message(message, player_two)\n",
    "            result = f\"Player 1: {response1.content}\\nPlayer 2: {response2.content}\\n\"\n",
    "            judgement = f\"{JUDGE}{result}Who wins?\"\n",
    "            message = TextMessage(content=judgement, source=\"user\")\n",
    "            response = await self._delegate.on_messages([message], ctx.cancellation_token)\n",
    "            return Message(content=result + response.chat_message.content)\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "runtime = SingleThreadedAgentRuntime()\n",
    "await PlayerOneAgent.register(runtime, \"player_one\", lambda: PlayerOneAgent(\"player_one\"))\n",
    "await PlayerTwoAgent.register(runtime, \"player_two\", lambda: PlayerTwoAgent(\"player_two\"))\n",
    "await RockPaperScissorsAgent.register(runtime, \"rock_paper_scissors\", lambda: RockPaperScissorsAgent(\"rock_paper_scissors\"))\n",
    "runtime.start()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "agent_id = AgentId(\"rock_paper_scissors\", \"default\")\n",
    "message = Message(content=\"go\")\n",
    "response = await runtime.send_message(message, agent_id)\n",
    "print(response.content)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "await runtime.stop()\n",
    "await runtime.close()"
   ]
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": ".venv",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.12.1"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 2
}
